package com.awenhui.demo.filter;

import com.alibaba.fastjson.JSONObject;
import com.awenhui.demo.core.base.ActionResult;
import com.awenhui.demo.core.base.ResultCode;
import com.awenhui.demo.core.utils.zutils.ServletUtils;
import com.awenhui.demo.core.utils.zutils.convert.ConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 数据签名过滤器
 *
 * @author zhouxuanhong
 * @date 2019-05-20 21:51
 * @since JDK1.8
 */
@Slf4j
@Component
@Order(1)
public class SignatureFilter extends OncePerRequestFilter {
    //    @Value("${mc.feign-uri}")
//    private String mcUrl;
    private String mcUrl = "http://localhost:3000";
    private static final String OPEN = "OPEN";
    private static final String GET = "GET";
    private static final String POST = "POST";
    private static final String CHECK_SIGN_URI = "/oauth/check-sign";


    @Autowired
    private RestTemplate restTemplate;

    //    @Value("${app.auth-sign-check}")
//    private boolean authSignCheck;
    private boolean authSignCheck = false;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        BodyReaderRequestWrapper bodyReaderRequestWrapper = null;
        // 开放平台授权登录，数据交付时需要进行单项数据验签
        if (authSignCheck && checkSign(request)) {
            String method = request.getMethod();
            boolean result = false;
            // 处理post验签
            if (POST.equalsIgnoreCase(method)) {
                // 重写request以便数据流复用
                bodyReaderRequestWrapper = new BodyReaderRequestWrapper(request);
                result = verifySignPost(bodyReaderRequestWrapper, response);
            }
            // 处理get验签
            else if (GET.equalsIgnoreCase(method)) {
                result = verifySignGet(bodyReaderRequestWrapper, response);
            } else {
                ServletUtils.writerResponse(ResultCode.UNKNOWN_ERROR.getValue(), "请求方法不支持,只能使用post或get", response);
            }
            //验签失败，不再向下传递，返回响应结果
            if (!result) {
                return;
            }
        }
        filterChain.doFilter(bodyReaderRequestWrapper != null ? bodyReaderRequestWrapper : request, response);
    }


    /**
     * post请求验签
     *
     * @param bodyReaderRequestWrapper ${@link BodyReaderRequestWrapper}
     * @param response                 ${@link HttpServletResponse}
     * @return boolean ${@link Boolean}
     * @author zhouxuanhong
     * @date 2019-05-28 21:27
     */
    private boolean verifySignPost(BodyReaderRequestWrapper bodyReaderRequestWrapper, HttpServletResponse response) throws IOException {
        JSONObject jsonObjectBody = bodyReaderRequestWrapper.getJsonObjectBody();
        HashMap<String, String> map = ConvertUtil.map(jsonObjectBody, HashMap.class);
        SortedMap<String, String> sort = new TreeMap<>(map);
        // 验签基本参数校验
        boolean result = checkParams(sort, response);
        if (!result) {
            return false;
        }
        // 远程验签
        return postRemoteCheckSign(jsonObjectBody, bodyReaderRequestWrapper, response);
    }


    /**
     * 处理get请求验签
     *
     * @param request  ${@link HttpServletRequest}
     * @param response ${@link HttpServletResponse}
     * @return boolean ${@link Boolean}
     * @author zhouxuanhong
     * @date 2019-05-29 09:51
     */
    private boolean verifySignGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 处理get参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 验签基本参数校验
        SortedMap<String, String> treeMap = new TreeMap<>();
        parameterMap.forEach((k, v) -> treeMap.put(k, v[0]));
        boolean result = checkParams(treeMap, response);
        if (!result) {
            return false;
        }
        // 远程验签
        return getRemoteCheckSign(request, response);
    }

    /**
     * 签名基本参数校验
     *
     * @param treeMap ${@link SortedMap <String,String>}
     * @return Object ${@link Object}
     * @author zhouxuanhong
     * @date 2019-05-29 09:51
     */
    public static boolean checkParams(SortedMap<String, String> treeMap, HttpServletResponse response) throws IOException {
        Object timestamp = treeMap.get("timestamp");
        Object sign = treeMap.get("sign");
        String msg = "";
        int code = 0;
        boolean result = true;
        if (sign == null || StringUtils.isBlank(sign.toString())) {
            code = ResultCode.API_SIGN_MISS_ERROR.getValue();
            msg = ResultCode.API_SIGN_MISS_ERROR.getDescription();
            result = false;
        }
        if (timestamp == null || StringUtils.isBlank(timestamp.toString())) {
            code = ResultCode.API_SIGN_MISS_ERROR.getValue();
            result = false;
            if (StringUtils.isNotBlank(msg)) {
                msg = msg + ",缺少timestamp参数";
            } else {
                msg = "缺少timestamp参数";
            }
        }
        if (!result) {
            ServletUtils.writerResponse(code, msg, response);
        }
        return result;
    }

    /**
     * get验签(验签服务器签名校验)
     *
     * @param request ${@link HttpServletRequest}
     * @return boolean ${@link Boolean}
     * @author zhou
     * @date 2020-01-13 16:31
     */
    public boolean getRemoteCheckSign(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String queryString = request.getQueryString();
        String url = mcUrl + CHECK_SIGN_URI;
        if (StringUtils.isNotBlank(queryString)) {
            url += url + "?" + queryString;
        }
        HttpEntity<?> httpHeaders = getHttpHeaders(request, null);
        ResponseEntity<ActionResult> forEntity = restTemplate.getForEntity(url, ActionResult.class, httpHeaders);
        return dealWithSignResult(forEntity, response);
    }

    /**
     * 处理post验签(验签服务器签名校验)
     *
     * @param jsonObjectBody ${@link HttpServletRequest}
     * @return boolean ${@link Boolean}
     * @author zhou
     * @date 2020-01-13 16:33
     */
    public boolean postRemoteCheckSign(JSONObject jsonObjectBody, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String queryString = request.getQueryString();
        String url = mcUrl + CHECK_SIGN_URI;
        if (StringUtils.isNotBlank(queryString)) {
            url += url + "?" + queryString;
        }
        HttpEntity<?> httpHeaders = getHttpHeaders(request, jsonObjectBody);
        ResponseEntity<ActionResult> forEntity = restTemplate.postForEntity(url, httpHeaders, ActionResult.class);
        log.debug("------------SignatureFilter------验签服务器验签结果------>postCheckSign:{}", forEntity);
        return dealWithSignResult(forEntity, response);
    }

    /**
     * 判断是否需要验签
     *
     * @param request ${@link HttpServletRequest}
     * @author zhou
     * @date 2020-01-13 18:47
     */
    private boolean checkSign(HttpServletRequest request) {
        String uri = request.getRequestURI();
        if (StringUtils.isNotBlank(uri)) {
            uri = uri.toUpperCase();
            if (uri.endsWith(OPEN)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 组装当前请求头信息与参数
     *
     * @param request ${@link HttpServletRequest}
     * @param t       ${@link T}
     * @return HttpEntity<String> ${@link HttpEntity <String>}
     * @author zhou
     * @date 2020-01-13 17:21
     */
    private <T> HttpEntity<?> getHttpHeaders(HttpServletRequest request, T t) {
        Enumeration<String> headerNames = request.getHeaderNames();
        HttpHeaders headers = new HttpHeaders();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headers.add(headerName, request.getHeader(headerName));
        }
        return new HttpEntity<>(t, headers);
    }


    /**
     * 处理验签结果
     *
     * @param forEntity ${@link ResponseEntity<ActionResult>}
     * @param response  ${@link HttpServletResponse}
     * @author zhou
     * @date 2020-01-13 17:39
     */
    private boolean dealWithSignResult(ResponseEntity<ActionResult> forEntity, HttpServletResponse response) throws IOException {
        boolean checkResult = false;
        if (forEntity.getStatusCode() == HttpStatus.OK) {
            ActionResult body = forEntity.getBody();
            if (body.isSuccess()) {
                checkResult = true;
            } else {
                ServletUtils.writerResponse(body.getCode(), body.getMessage(), response);
            }
        } else {
            ServletUtils.writerResponse(ResultCode.API_ERROR.getValue(), "验签服务器调用失败", response);
        }
        return checkResult;
    }
}
